# -*- coding: utf-8 -*-
#some player handeling perhaps?

import pygame
import sys
import math
import os

import data
import weapon
import default
import level
import animation

class Falling:
    pass

#this should probably be the base-class for the player class, but right now... well never mind
class Character(pygame.sprite.Sprite):
    def __init__(self, anim, pos, coord_scale, vel=(0,0), state=Falling()):
        pygame.sprite.Sprite.__init__(self)

        # An animation is a strip of images, each having
        # its place in the image matrix. Yes matrices ARE
        # what we want here! example: self.animation.falling, shooting.
        # These states are defined by the state machine of the character
        #self.animation, self.rect = animation.load(image, -1)
        #self.surface = self.animation.get(state)

        #this was added for effects.py
        #if type(image) is pygame.Surface:
            #self.image = image
            #self.rect = self.image.get_rect()
        #else: 
            #self.image, self.rect = data.load_image(image, -1)
        if type(anim) == animation.Animation:
            self.animation = anim
        else:    
            self.animation = animation.Animation(anim)
        

        self.image, self.rect  = self.animation.image, self.animation.rect
        #real coordinates, we keepp self.rect for itself, just in case:
        
        self.world_pos = pygame.Rect(pos, self.rect.size)
        #small, but sexy hack
        self.world_pos.bottomleft = pos
        
        self.vel = vel
        self.max_vel = 100
        
        self.onground = False #until we know better

        self.coord_scale = coord_scale

        #If this is True, everything that touches it will die
        self.killer = False
        
        self.direction = "right"
         
        
    def update(self, camera, curr_level = None, t=1):
        """Update position according to speed, and check for collisions.
        Also prepare for drawing, by using camera to calculate screen coordinates"""

        image = self.animation.next()
        if image is not None:

            self.image, randomrect = image

            if self.direction is "left":
                self.image = pygame.transform.flip(self.image, True, False)

        
        old = pygame.Rect(self.world_pos)
        
        #Friction
        if abs(self.vel[0]) > default.friction:
            #We have no mass...
            self.accelerate((-default.friction*abs(self.vel[0])/self.vel[0], 0))
        else:
            self.vel = (0, self.vel[1]) #The force is less than the fully developed friction, so we stop
        #Gravity
        if not self.onground:
            self.accelerate((0, default.g))

        if abs(self.vel[0]) > self.max_vel:
            self.vel = (self.max_vel*abs(self.vel[0])/self.vel[0], self.vel[1])
        if abs(self.vel[1]) > self.max_vel:
            self.vel = (self.vel[0], self.max_vel*abs(self.vel[1])/self.vel[1])
        self.world_pos.move_ip(t*self.vel[0], t*self.vel[1])  
        
        #React to level environment
        if curr_level:
            #if self.outside(curr_level): #player has fallen to death
            #    self.die()                            # and must be terminated!!!
            #    sys.exit(0)
                
            #This also uses self.rect... We recalculate other values as well, so we shouldn't mind
            self.rect.bottomleft = self.world_pos.bottomleft
            tiles = pygame.sprite.spritecollide(self, curr_level.tile_group, False)

            my = self.world_pos

            top_bottom = False #True if we touch the ground or roof
                    
            for point in (my.topleft, my.topright, my.bottomleft, my.bottomright,
                          my.midleft, my.midright, my.midbottom, my.midtop):

                #Check if point intersects with any of the tiles which overlap the character
                for tile in tiles:
                
                    its = tile.rect
                    if (old.bottom <= its.top and my.bottom > its.top):
                        my.bottom = its.top
                        if self.vel[1] > 0:
                            self.vel = (self.vel[0], 0)
                        self.onground = True
                        top_bottom = True
                    if (old.right <= its.left and my.right > its.left):
                        my.right = its.left
                        if self.vel[0] > 0 and not top_bottom:
                            self.vel = (0, self.vel[1])
                    if (old.left >= its.right and my.left < its.right):
                        my.left = its.right
                        if self.vel[0] < 0 and not top_bottom:
                            self.vel = (0, self.vel[1])
                    if (old.top >= its.bottom and my.top < its.bottom): #Bottom last, dammit!
                        my.top = its.bottom
                        if self.vel[1] < 0:
                            self.vel = (self.vel[0], 0)
                        top_bottom = True
                    
            if len(tiles) == 0:
                self.onground = False
            
        # We need to convert the world corrdinates to screen coords, for drawing
        x = ((self.world_pos.x - camera.x))# / self.coord_scale)
        y = ((self.world_pos.y - camera.y))# / self.coord_scale)
        
        self.rect.topleft = (x, y)
        

    def accelerate(self, acc=(0,0), t=1):
        """Accelerate according to vector acc for t ticks"""
        
        self.vel = (self.vel[0] + t*acc[0], self.vel[1] + t*acc[1])

    #def face(direction):
    
    def die(self, curr_level = None, cause="implement later"):
        self.kill()
        #sys.exit(0) #just temporary

    def outside(self, curr_level):
        """Return True if character is outside screen"""
                
        return self.world_pos.top > curr_level.levelrect.bottom



import effects #Which happens to be a character

class Player(Character):
    """A character that can handle weapons, has life, state,
    and functions that the user may request"""
    def __init__(self, image, pos, coord_scale, vel=(0,0), hp=100,
                 weapons=[weapon.Gun(), weapon.Gun()], state=Falling()):
        """image: image data file to load
        pos: position in world
        vel: starting velocity
        hp: character life
        weapons: list of classes of all the weapons that player carries"""
        Character.__init__(self, image, pos, coord_scale, vel, state)

        self.animations = { "standing" : animation.Animation(os.path.join("robot", "standing")),
                            "walking" : animation.Animation(os.path.join("robot", "walking")), 
                            "attacking" : animation.Animation(os.path.join("robot", "sawing"))}

        self.animation = self.animations["standing"]
        
        self.hp = hp
        self.dead = False
        self.weapons = weapons
        
        if self.weapons:
            self.weapon = self.weapons[0]
            #self.weapon = self.next_weapon()

        self.state = state

        self.next_state = None

        self.killer = True
        
        self.max_vel = 20
        
        self.go = (0, 0)
        
        self.jetmax = default.jetmax        
        self.jet = self.jetmax + 0
        self.jetting = False
        self.attacking = 0


        #a surface representing the gas
        size = default.metersize
        self.jetmeter = pygame.Surface(size)
        self.jetmeter.set_alpha(200)
        
    def update(self, camera, curr_level = None, t=1):
        """Update player"""
        
        self.accelerate(self.go) #key input sets go

        self.dojet()
        

        Character.update(self, camera, curr_level, t)

        if self.next_state:
            state.change(next_state)

        self.update_jetmeter()
        
        if self.attacking > 0:
            self.attacking -= 1
            bullet = self.weapon.attack(self.world_pos.center, self)
            if bullet is not None:
                #print "bä"
                curr_level.player_bullets.add(bullet)

        #Change animation according to physics
        if self.vel[0] == 0 or not self.onground:
            self.change_animation("standing")
        if self.vel[0] != 0 and self.onground:
            self.change_animation("walking")
        #if self.jetting:
        #    self.change_animation("jumping")
        #if self.dead:
        #    self.change_animation("dying")
        if self.attacking:
            self.change_animation("attacking")


        #A weapnon has a state too
        #self.weapon.update(t)
    def change_animation(self, newanim):
        """Change character animation if it is not already using new animation"""
        
        if self.animation != self.animations[newanim]:
            self.animation = self.animations[newanim]

    def update_jetmeter(self):
        """Update self.meter surface"""
        
        width = default.metersize[0] - 10
        height = default.metersize[1] - 10

        percentage = float(self.jet)/self.jetmax
        
        self.jetmeter.fill( (237, 208, 71) )
        
        full = pygame.Surface(( width, height ))
        current = pygame.Surface(( width, int(math.floor(height * percentage) )))

        full.fill( (157, 150, 150) )

        #Pick color depending on remaining capacity
        if percentage >= 1:
            color = ( 64, 210, 0 )
        elif percentage >= 0.75:
            color = ( 248, 205, 3 )
        elif percentage >= 0.25:
            color = ( 244, 128, 4 )
        else:
            color = ( 244, 15, 4 )
        current.fill( color )

        self.jetmeter.blit( full, (5, 5) )
        self.jetmeter.blit( current, ( 5, 5 + (full.get_height() - current.get_height())) )
        
    def change_weapon(self, index):
        """Change to weapon indicated by index, or return false if
        no such weapon in inventory."""
        if weapons[index]:
            self.weapon = weapons[index]
            return True
        else:
            return False
        
    def aim(self, angle):
        """Aim in the angle (radians), where 0 is right ahead"""
        self.weapon.aim(angle)
        
    def attack(self, key_press):
        """Fire weapon"""
        if key_press:
            self.attacking = 10

    ## Requests from user
    def jetpack(self, key_press):
        """Use jetpack"""
        if key_press:
            #self.accelerate((0, -10))
            self.jetting = True
        else:
            self.jetting = False
                        
    def left(self, key_press):
        """Accelerate player to the left"""
        self.direction = "left"
        if key_press:
            # So far you can only push him sideways by pressing the button
            #(BUT IF YOU DO IT FAST ENOUGH, YOU CAN MAKE HIM RUN LIKE THE WIND!)
            self.go = (-default.player_acc, 0)
        else:
            self.go = (0, 0)
        
    def right(self, key_press):
        """Accelerate player to the right"""
        self.direction = "right"
        if key_press:
            # No magic
            #self.accelerate((6, 0))
            self.go = (default.player_acc, 0)
        else:
            self.go = (0, 0)                     
                    
    def dojet(self):
        if self.jetting:

            self.accelerate((0, -default.jet_acc))

            self.jet -= default.jetdrain
            if self.jet <= 0:
                self.jetting = False
                self.jet = 0
        elif self.onground and self.jet < self.jetmax:
                self.jet += default.jetreturn
        
class Enemy(Character):       #just testing... Wait, what? The player is an enemy? And I've been defending him all along!
    def __init__(self, image, pos, coord_scale, period, direction,\
                 vel=(0,0), hp=100, state=Falling()):
        """Period is the number of frames to walk before turning
        direction: "left" or "right" """
        Character.__init__(self, image, pos, coord_scale, vel=(0,0), #hp=100,
                         state=Falling())#weapons=None,

        
        self.animations = { "standing" : animation.Animation(os.path.join("human", "walking")),
                            "sawed" : animation.Animation(os.path.join("human", "sawed")),
                            "dead" : animation.Animation(os.path.join("human", "dead"))}
    
        self.animation = self.animations["standing"]
    
        self.period = period
        self.walked = 0
        self.direction = direction
        
        self.dead = False
        #hardcoding in this value. Too...little...time!
        self.deadtimer = 12

        
        self.killer = False
        
    def update(self, camera, curr_level = None, t=1):
        

        #Make him go back and forth, back and forth, back and forth, back and forth....
        if self.walked == 0: #if we begin one period, make him believe we pressed the button 
            if self.direction == "left":
                #Rushed, rushed, rushed
                self.vel = (-1, self.vel[1])   #left and right have some nice cheats for keeping a constant speed
            else:
                self.vel = (1, self.vel[1])
        elif self.walked > self.period: #if we are finished
            if self.direction == "left":
                self.direction = "right"
                self.walked = 0
                #self.left(False) #We let go of the button
            else:
                self.direction = "left"
                self.walked = 0
                #self.right(False)
        else:
            self.walked += 1
        
        if self.dead:
            if self.deadtimer > 0:
                self.deadtimer -= 1
            else:
                if not self.animation == self.animations["dead"]:
                    self.change_animation("dead")
                    self.deadtimer = 10 #stay dead for awhile
                else: 
                    Character.die(self, curr_level)

        Character.update(self, camera, curr_level, t=1)
        
    def die(self, curr_level):
        curr_level.effects_group.add(effects.gore_splash(self.world_pos.center))
        self.dead = True

    def change_animation(self, newanim):
        """Change character animation if it is not already using new animation"""
        if self.animation != self.animations[newanim]:
            self.animation = self.animations[newanim]

        
        
